/*
 * @Descripttion: YML解析
 * @version:
 * @Author: lfzxs@qq.com
 * @Date: 2022-11-22 15:43:04
 * @LastEditors: lfzxs@qq.com
 * @LastEditTime: 2023-03-24 15:50:08
 */
package cfg

import (
	"context"
	"io/ioutil"
	"os"
	"reflect"
	"strings"

	"gitee.com/g_boot/gfextend/common/system"
	"github.com/pkg/errors"
	"gopkg.in/yaml.v2"
)

var (
	// Prefix array for trying searching in local system.
	localSystemTryFolders = []string{"", "config/", "manifest/config/"}
)

type YmlCfg struct {
	cfg
}

func ParseYML(buf []byte) (data map[string]interface{}, err error) {
	// 要有顺序,否则变量解析会出错
	cnf := yaml.MapSlice{}

	err = yaml.Unmarshal(buf, &cnf)
	if err != nil {
		return nil, err
	}

	//PrintYml(cnf)
	data = ReplaceExp(data, cnf)
	return data, err
}

func ReplaceExp(env map[string]interface{}, content yaml.MapSlice) map[string]interface{} {
	result := make(map[string]interface{})

	for _, item := range content {
		key := item.Key.(string)
		switch value := item.Value.(type) { // value 表示 item.Value 转换成对应 type 的值
		case string: // item.Value 是字符串类型
			result[key] = ExpandValueAnalysis(result, value)
		case yaml.MapSlice: // item.Value 是yaml.MapSlice类型
			result[key] = ReplaceExp(result, value)
		case []interface{}: // []interface{} 类型
			var vals []interface{}
			for _, subint := range value {
				vs, ok := subint.(string)
				if ok {
					vals = append(vals, ExpandValueAnalysis(result, vs))
					result[key] = vals
				}

				vMapSlice, ok := subint.(yaml.MapSlice)
				if ok {
					result[key] = ReplaceExp(result, vMapSlice)
				}
			}

		default: // 未知类型
			result[key] = value
		}
	}
	return result
}

func ExpandValueAnalysis(env map[string]interface{}, value string) (realValue string) {
	realValue = value

	vLen := len(value)
	// 3 = ${}
	if vLen < 3 {
		return
	}

	temp := value
	result := ""
	exp := ""

	exp_start := strings.Index(temp, "${")
	exp_end := strings.Index(temp, "}")
	vlen := len(temp)

	for exp_start >= 0 && exp_end > exp_start {
		result = result + temp[0:exp_start]

		exp = temp[exp_start : exp_end+1]

		vlen = len(temp)
		temp = temp[exp_end+1 : vlen]
		result = result + ExpandExpEnv(env, exp)

		exp_start = strings.Index(temp, "${")
		exp_end = strings.Index(temp, "}")
	}
	result = result + temp

	return result
}

// 解析规则表达式
func ExpandExpEnv(env map[string]interface{}, exp string) (realValue string) {
	vLen := len(exp)
	key := ""
	defaultV := ""
	// value start with "${"
	for i := 2; i < vLen; i++ {
		if exp[i] == ':' {
			key = exp[2:i]
			defaultV = exp[i+1 : vLen-1] // other string is default value.
			break
		} else if exp[i] == '}' {
			key = exp[2:i]
			break
		}
	}

	realValue, ok := env[key].(string)

	if !ok {
		realValue = os.Getenv(key)
		if realValue == "" {
			realValue = defaultV
		}
	}
	return

}

// 从多个目录加载
func (self *YmlCfg) LoadFromFolders(fileName string, searchFolds ...string) (err error) {
	if len(searchFolds) == 0 {
		searchFolds = localSystemTryFolders
	}

	rootPath := system.RootPath()
	for _, value := range searchFolds {
		absolutePath := rootPath + "/" + value + fileName
		if system.FileExists(absolutePath) {
			return self.LoadFromFile(absolutePath)
		}
	}
	err = errors.Errorf("no search paths:%s,%v", rootPath, searchFolds)
	return
}

// 加载指定文件
func (self *YmlCfg) LoadFromFile(filePath string) (err error) {
	content, err := ioutil.ReadFile(filePath)
	if err != nil {
		return err
	}
	return self.LoadData(content)
}

// 加载
func (self *YmlCfg) LoadData(data []byte) (err error) {
	self.data, err = ParseYML(data)
	return
}

func (self *YmlCfg) Available(ctx context.Context, resource ...string) (ok bool) {
	return true
}

func (self *YmlCfg) Get(ctx context.Context, pattern string) (value interface{}, err error) {
	value = self.GetValue(pattern)
	return value, err
}

func (self *YmlCfg) Data(ctx context.Context) (data map[string]interface{}, err error) {
	data = self.data
	return
}

func (self *YmlCfg) GetValue(name string) interface{} {
	path := strings.Split(name, ".")
	data := self.data
	for key, value := range path {
		v, ok := data[value]
		if !ok {
			break
		}
		if (key + 1) == len(path) {
			return v
		}
		if reflect.TypeOf(v).String() == "map[string]interface {}" {
			data, ok = v.(map[string]interface{})
		}
	}
	return nil
}
